package cn.net.yato.picture.controller;

import cn.net.yato.picture.biz.SysBiz;
import cn.net.yato.picture.biz.UserBiz;
import cn.net.yato.picture.config.shiro.EmailAndVerificationCodeToken;
import cn.net.yato.picture.config.shiro.PhoneAndVerificationCodeToken;
import cn.net.yato.picture.config.shiro.WeCatOpenIdToken;
import cn.net.yato.picture.constant.CommParams;
import cn.net.yato.picture.entity.LoginLog;
import cn.net.yato.picture.entity.User;
import cn.net.yato.picture.util.RandomUtil;
import cn.net.yato.picture.util.RedisUtil;
import cn.net.yato.picture.util.email.EmailUtil;
import cn.net.yato.picture.util.encryption.DateTools;
import cn.net.yato.picture.util.phone.QcloudSmsUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName LoginController
 * @Description TODO
 * @Author yato
 * @Date 2020/2/17 16:04
 * @Version 1.0
 */
@RestController
public class LoginController {

    @Resource
    private SysBiz sysBiz;
    @Resource
    private UserBiz userBiz;
    @Autowired
    private QcloudSmsUtil qcloudSmsUtil;
    @Autowired
    private EmailUtil emailUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("/login.html")
    public ModelAndView login(ModelAndView modelAndView) {
        modelAndView.setViewName("login");

        return modelAndView;
    }

    @GetMapping("/logout")
    public void logout(HttpServletResponse response, HttpSession session) throws IOException {
        session.removeAttribute(CommParams.LOGIN_SESSION_KEY);
        SecurityUtils.getSubject().logout();
        response.sendRedirect("login.html");
    }

    @PostMapping("/sendEmail.json")
    public Object sendEmail(String email) {
        JSONObject json = new JSONObject();
        json.put(CommParams.LOGIN_CODE, 0);
        /*验证手机号码是否绑定账户*/
        User user = sysBiz.loginByEmailBiz(email);
        if (user == null) {
            //返回邮箱账户不存在
            json.put(CommParams.LOGIN_MSG, "邮箱暂未绑定生效!");
            return json.toString();
        }
        String code = String.valueOf((int) RandomUtil.getRandomNum(6));
        /*将验证码存入redis*/
        redisUtil.set(CommParams.LOGIN_CODE + email, new Md5Hash(code, CommParams.REDIS_EMAIL_CODE, CommParams.REDIS_CODE_SALTCOUNT).toString(), 60);
        /*记录发送次数*/
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        opsForValue.increment(email + CommParams.LOGIN_EMAIL_STR, 1);
        if (Integer.parseInt(opsForValue.get(email + CommParams.LOGIN_EMAIL_STR)) > CommParams.LOGIN_EMAIL_MAXCOUNT) {
            opsForValue.set(email + CommParams.EMAIL_IS_LOCK, CommParams.EMAIL_SHIRO_LOCK);
            stringRedisTemplate.expire(email + CommParams.EMAIL_IS_LOCK, 1, TimeUnit.HOURS);
            opsForValue.set(email + CommParams.LOGIN_EMAIL_STR, "0");
        }
        if (CommParams.EMAIL_SHIRO_LOCK.equals(opsForValue.get(email + CommParams.EMAIL_IS_LOCK))) {
            json.put("msg", "由于您的频繁操作！账户锁定1小时!");
        }
        boolean faly = false;
        try {
            faly = emailUtil.sendMail(code, "2", email);
            json.put(CommParams.LOGIN_CODE, faly ? 1 : 0);
            json.put(CommParams.LOGIN_MSG, faly ? "邮件发送成功!有效时间2分钟!" : "邮件发送失败!");
        } catch (Exception e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, "未知异常,邮件发送失败!");
            return json.toString();
        }
        return json.toString();
    }

    @PostMapping("/sendSMS.json")
    public Object sendSMS(String phone) {
        JSONObject json = new JSONObject();
        json.put(CommParams.LOGIN_CODE, 0);
        /*验证手机号码是否绑定账户*/
        User user = sysBiz.loginByPhoneBiz(phone);
        if (user == null) {
            //返回手机号码不存在
            json.put(CommParams.LOGIN_MSG, "手机号码暂未绑定生效!");
            return json.toString();
        }
        String[] phoneNumbers = {phone};
        String code = String.valueOf((int) RandomUtil.getRandomNum(6));
        String[] params = {code, "1"};
        /*将验证码存入redis*/
        redisUtil.set(CommParams.LOGIN_CODE + phone, new Md5Hash(code, CommParams.REDIS_PHONE_CODE, CommParams.REDIS_CODE_SALTCOUNT).toString(), 60);
        /*记录发送次数*/
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        opsForValue.increment(phone + CommParams.LOGIN_PHONE_STR, 1);
        if (Integer.parseInt(opsForValue.get(phone + CommParams.LOGIN_PHONE_STR)) > CommParams.LOGIN_PHONE_MAXCOUNT) {
            opsForValue.set(phone + CommParams.PHONE_IS_LOCK, CommParams.PHONE_SHIRO_LOCK);
            stringRedisTemplate.expire(phone + CommParams.PHONE_IS_LOCK, 1, TimeUnit.HOURS);
            opsForValue.set(phone + CommParams.LOGIN_PHONE_STR, "0");
        }
        if (CommParams.PHONE_SHIRO_LOCK.equals(opsForValue.get(phone + CommParams.PHONE_IS_LOCK))) {
            json.put("msg", "由于您的频繁操作！账户锁定1小时!");
        }
        boolean faly = false;
        try {
            faly = qcloudSmsUtil.sendSms(phoneNumbers, params);
            json.put(CommParams.LOGIN_CODE, faly ? 1 : 0);
            json.put(CommParams.LOGIN_MSG, faly ? "短信发送成功!短信有效时间1分钟!" : "短信发送失败!");
        } catch (Exception e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, "未知异常,短信发送失败!");
            return json.toString();
        }
        return json.toString();
    }

    @PostMapping("/doLogin.json")
    public Object doLogin(String userName, String passWord, boolean remember, String loginType, HttpSession session,
                          String ip, String address) {
        JSONObject json = new JSONObject();
        json.put(CommParams.LOGIN_CODE, 0);
        try {
            AuthenticationToken authenticationToken = remember ? new UsernamePasswordToken(userName, passWord, remember) : new UsernamePasswordToken(userName, passWord);
            /*shiro认证*/
            SecurityUtils.getSubject().login(authenticationToken);
            User user = (User) SecurityUtils.getSubject().getPrincipal();
            /*写入登录日志*/
            LoginLog loginLog = new LoginLog();
            loginLog.setLoginDate(new Date());
            loginLog.setUserId(user.getUserId());
            loginLog.setIp(ip);
            loginLog.setLoginType(loginType);
            loginLog.setLoginAddress(address);
            int item = sysBiz.addLoginLogBiz(loginLog);
            if (item == 0) {
                json.put(CommParams.LOGIN_MSG, "Login Error Code 1002 addLoginLog");
                return json.toString();
            }
            /*记录最后一次登录日期*/
            user.setLastLoginDate(DateTools.getCurrentDate(CommParams.DATE_FORMAT_STR_ONE));
            item = userBiz.modifyLastLoginDateBiz(user);
            if (item == 0) {
                json.put(CommParams.LOGIN_MSG, "Login Error Code 1002 modifyLastLoginDate");
                return json.toString();
            }
            session.setAttribute(CommParams.LOGIN_SESSION_KEY, user);
            json.put("token", session.getId());
            json.put(CommParams.LOGIN_MSG, "登录成功!");
            json.put(CommParams.LOGIN_CODE, 1);
        } catch (LockedAccountException e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, e.getMessage());
            return json.toString();
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, e.getMessage());
            return json.toString();
        } catch (DisabledAccountException e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, e.getMessage());
            return json.toString();
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, e.getMessage());
            return json.toString();
        } catch (Exception e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, "未知异常!请刷新页面重试!");
            return json.toString();
        }
        return json.toString();
    }

    @PostMapping("/doPhoneLogin.json")
    public Object doPhoneLogin(String phone, String phoneYzm, String ip, String address, String loginType, HttpSession session) {
        JSONObject json = new JSONObject();
        json.put("code", 0);
        try {
            Object code = redisUtil.get(CommParams.LOGIN_CODE + phone);
            String outCode = new Md5Hash(phoneYzm, CommParams.REDIS_PHONE_CODE, CommParams.REDIS_CODE_SALTCOUNT).toString();
            if (code != null && code.equals(outCode)) {
                redisUtil.deleteStr(CommParams.LOGIN_CODE + phone);
                AuthenticationToken authenticationToken = new PhoneAndVerificationCodeToken(phone, true);
                SecurityUtils.getSubject().login(authenticationToken);
                User user = (User) SecurityUtils.getSubject().getPrincipal();
                /*写入登录日志*/
                LoginLog loginLog = new LoginLog();
                loginLog.setLoginDate(new Date());
                loginLog.setUserId(user.getUserId());
                loginLog.setIp(ip);
                loginLog.setLoginType(loginType);
                loginLog.setLoginAddress(address);
                int item = sysBiz.addLoginLogBiz(loginLog);
                if (item == 0) {
                    json.put(CommParams.LOGIN_MSG, "Login Error Code 1002 addLoginLog");
                    return json.toString();
                }
                /*记录最后一次登录日期*/
                user.setLastLoginDate(DateTools.getCurrentDate(CommParams.DATE_FORMAT_STR_ONE));
                item = userBiz.modifyLastLoginDateBiz(user);
                if (item == 0) {
                    json.put(CommParams.LOGIN_MSG, "Login Error Code 1002 modifyLastLoginDate");
                    return json.toString();
                }
                session.setAttribute(CommParams.LOGIN_SESSION_KEY, user);
                json.put("token", session.getId());
                json.put(CommParams.LOGIN_MSG, "登录成功!");
                json.put(CommParams.LOGIN_CODE, 1);
            } else {
                json.put(CommParams.LOGIN_MSG, "验证码已过期!请重新发送!");
            }
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, e.getMessage());
            return json.toString();
        } catch (Exception e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, "未知异常!请刷新页面重试!");
            return json.toString();
        }
        return json.toString();
    }

    @PostMapping("/doEmailLogin.json")
    public Object doEmailLogin(String email, String emailYzm, String ip, String address, String loginType, HttpSession session) {
        JSONObject json = new JSONObject();
        json.put("code", 0);
        try {
            Object code = redisUtil.get(CommParams.LOGIN_CODE + email);
            String outCode = new Md5Hash(emailYzm, CommParams.REDIS_EMAIL_CODE, CommParams.REDIS_CODE_SALTCOUNT).toString();
            if (code != null && code.equals(outCode)) {
                redisUtil.deleteStr(CommParams.LOGIN_CODE + email);
                AuthenticationToken authenticationToken = new EmailAndVerificationCodeToken(email, true);
                SecurityUtils.getSubject().login(authenticationToken);
                User user = (User) SecurityUtils.getSubject().getPrincipal();
                /*写入登录日志*/
                LoginLog loginLog = new LoginLog();
                loginLog.setLoginDate(new Date());
                loginLog.setUserId(user.getUserId());
                loginLog.setIp(ip);
                loginLog.setLoginType(loginType);
                loginLog.setLoginAddress(address);
                int item = sysBiz.addLoginLogBiz(loginLog);
                if (item == 0) {
                    json.put(CommParams.LOGIN_MSG, "Login Error Code 1002 addLoginLog");
                    return json.toString();
                }
                /*记录最后一次登录日期*/
                user.setLastLoginDate(DateTools.getCurrentDate(CommParams.DATE_FORMAT_STR_ONE));
                item = userBiz.modifyLastLoginDateBiz(user);
                if (item == 0) {
                    json.put(CommParams.LOGIN_MSG, "Login Error Code 1002 modifyLastLoginDate");
                    return json.toString();
                }
                session.setAttribute(CommParams.LOGIN_SESSION_KEY, user);
                json.put("token", session.getId());
                json.put(CommParams.LOGIN_MSG, "登录成功!");
                json.put(CommParams.LOGIN_CODE, 1);
            } else {
                json.put(CommParams.LOGIN_MSG, "验证码已过期!请重新发送!");
            }
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, e.getMessage());
            return json.toString();
        } catch (Exception e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, "未知异常!请刷新页面重试!");
            return json.toString();
        }
        return json.toString();
    }

    @PostMapping("/doWeCatLogin.json")
    public Object doWeCatLogin(String ip, String address, String loginType,HttpSession session) {
        JSONObject json = new JSONObject();
        json.put("code", 0);
        try {
            String openId = (String) session.getAttribute("openId");
            session.removeAttribute("openId");
            session.removeAttribute("openCode");
            AuthenticationToken authenticationToken = new WeCatOpenIdToken(openId, true);
            SecurityUtils.getSubject().login(authenticationToken);
            User user = (User) SecurityUtils.getSubject().getPrincipal();
            /*写入登录日志*/
            LoginLog loginLog = new LoginLog();
            loginLog.setLoginDate(new Date());
            loginLog.setUserId(user.getUserId());
            loginLog.setIp(ip);
            loginLog.setLoginType(loginType);
            loginLog.setLoginAddress(address);
            int item = sysBiz.addLoginLogBiz(loginLog);
            if (item == 0) {
                json.put(CommParams.LOGIN_MSG, "Login Error Code 1002 addLoginLog");
                return json.toString();
            }
            /*记录最后一次登录日期*/
            user.setLastLoginDate(DateTools.getCurrentDate(CommParams.DATE_FORMAT_STR_ONE));
            item = userBiz.modifyLastLoginDateBiz(user);
            if (item == 0) {
                json.put(CommParams.LOGIN_MSG, "Login Error Code 1002 modifyLastLoginDate");
                return json.toString();
            }
            session.setAttribute(CommParams.LOGIN_SESSION_KEY, user);
            json.put("token", session.getId());
            json.put(CommParams.LOGIN_MSG, "登录成功!");
            json.put(CommParams.LOGIN_CODE, 1);
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, e.getMessage());
            return json.toString();
        } catch (Exception e) {
            e.printStackTrace();
            json.put(CommParams.LOGIN_MSG, "未知异常!请刷新页面重试!");
            return json.toString();
        }
        return json.toString();
    }

    @GetMapping(value = "/weCatLogin.html")
    public void login(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
        String url = "https://open.weixin.qq.com/connect/qrconnect?";
        url += "appid=wx41b6e73cfab51639";
        url += "&redirect_uri=" + URLEncoder.encode("http://www.yato.net.cn/picture/weCatLoginCallback", "GBK");
        url += "&response_type=code&scope=snsapi_login";
        url += "&state=" + request.getSession().getId() + "#wechat_redirect";
        try {
            response.sendRedirect(url);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @GetMapping(value = "/weCatLoginCallback")
    public ModelAndView callBackLogin(HttpServletRequest request, ModelAndView modelAndView, HttpSession session) {
        JSONObject jsonObject = null;
        modelAndView.setViewName("login");
        try {
            String code = request.getParameter("code");
            String state = request.getParameter("state");
            String url = "https://api.weixin.qq.com/sns/oauth2/access_token?";
            url += "appid=wx41b6e73cfab51639";
            url += "&secret=559f897bfbf53525eaf8e3caaceacf98";
            url += "&code=" + code + "&grant_type=authorization_code";
            jsonObject = this.httpGet(url);
            String at = jsonObject.getString("access_token");
            String openId = jsonObject.getString("openid");
            url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + at + "&openid=" + openId;
            jsonObject = this.httpGet(url);
            session.setAttribute("openId", openId);
            session.setAttribute("openCode", "1");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return modelAndView;
    }

    public JSONObject httpGet(String url) {
        // get请求返回结果
        JSONObject jsonResult = new JSONObject();
        try {
            CloseableHttpClient client = HttpClients.createDefault();
            // 发送get请求
            HttpGet request = new HttpGet(url);
            HttpResponse response = client.execute(request);
            /** 请求发送成功，并得到响应 **/
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                /** 读取服务器返回过来的json字符串数据 **/
                String strResult = EntityUtils.toString(response.getEntity());
                String str = new String(strResult.getBytes("ISO-8859-1"), "UTF-8");
                /** 把json字符串转换成json对象 **/
                jsonResult = JSON.parseObject(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jsonResult;
    }

}
